package com.server.runtime;

import gnu.cajo.invoke.Remote;
import java.rmi.RemoteException;
import java.util.ArrayList;

import combat.protocol.interfaces.ProjectileInt;
import combat.protocol.interfaces.VehicleInt;
import combat.protocol.data.PhysicalObject;
import combat.protocol.data.RemoteList;
import combat.protocol.interfaces.SimulationInt;
import combat.protocol.interfaces.TeamInt;
import combat.protocol.interfaces.TrackInt;
import combat.protocol.interfaces.UserInt;
import com.server.projectiles.projectileImpl;
import com.server.runtime.vehicleImpl;

public class simulationImpl implements SimulationInt, Runnable{
	
	private static simulationImpl instance = null;
	private static ArrayList<TeamInt> teamList = new ArrayList<TeamInt>();
    
    private static Thread runner;

	private static Boolean isRunning = false;
	private static double gameTime = 0;
    private static double speedMultiplier = 1.0;   
    
	public void run() {
		 // Only start if not started yet
    	if(runner==null) {
        	//Create a new Thread with this instance
        	runner = new Thread(this, "SimulatorThread");
                    
        	//Start the new thread (this will start the new thread to begin its life by calling run())
        	runner.start();
         	System.out.println("UPDATE: Simulator started");
        } else {
        	System.out.println("WARNING: Simulator is already running.");
        }
	}

	public synchronized static simulationImpl getReference () {
		if(instance == null) {
			instance = new simulationImpl();
		}
		return instance;
	}
	
	public synchronized void setGameTime(double gameTime) {
		simulationImpl.gameTime = gameTime;
	}

	public synchronized double getGameTime() {
		return gameTime;
	}
	
	public synchronized Double getSimSpeed() {
		return speedMultiplier;
	}

	public synchronized void setSimSpeed(Double simSpeed) {
		simulationImpl.speedMultiplier = simSpeed;
        System.out.println("UPDATE: Game speed: " + this.getSimSpeed());
	}
	
	public synchronized void startSimulation() {
		speedMultiplier = 1.0;
        System.out.println("UPDATE: Game started");
	}
	
	public synchronized void pauseSimulation() {
		speedMultiplier = 0;
        System.out.println("UPDATE: Game paused");
	}
	
	public synchronized Boolean getIsRunning() {
		return isRunning;
	}

	public synchronized void setIsRunning(Boolean isRunning) {
		simulationImpl.isRunning = isRunning;
	}

	public synchronized void removeTeam(int teamID) {
		teamList.remove(teamID);
	}

	public synchronized void stopSimulation() {
		//Set running Boolean
		pauseSimulation();
		setIsRunning(false);
		
		//Do everything else
	}
	
	public synchronized ArrayList<TeamInt> getTeams() {
		return teamList;
	}

	public synchronized void setTeams(ArrayList<TeamInt> teams) {
		teamList = teams;
	}

	public synchronized void addTeam(TeamInt team) {
		teamList.add(team);
	}

	public synchronized void removeTeam(TeamInt team) {
		teamList.remove(team);
	}
	
	@Override
	public ArrayList<PhysicalObject> update(TeamInt team) {
		ArrayList<PhysicalObject> physObjects = new ArrayList<PhysicalObject>();
		for (TeamInt selectTeam : teamList) {
			if (selectTeam.equals(team)) {
				// Add all PhysicalObjects of the team in parameter in the ArrayList
				for (VehicleInt vehicle : team.getVehicles()) {
					physObjects.add((vehicleImpl) vehicle);
				}
				for (ProjectileInt projectile : team.getProjectiles()) {
					physObjects.add((projectileImpl) projectile);
				}
				for (TrackInt track : team.getTracks()) {
					physObjects.add((trackImpl) track);
				}
			} 
		}
		
		return physObjects;
	}
	
	@Override
	public synchronized UserInt userLogOn(String userName) {
		userImpl user = new userImpl(simulationImpl.getReference().getTeams().get(0).getUsers().size(), userName);
		teamList.get(2).addUser(user);
		return user;
	}

	@Override
	public synchronized TeamInt findTeamOfUser(UserInt user) {
		ArrayList<TeamInt> teams = this.getTeams();
		for (TeamInt team : teams) {
			ArrayList<UserInt> users = team.getUsers();
			for (UserInt selecteduser : users)
				if (selecteduser.getUserName().equals(user.getUserName()))
					return team;
		}
		return null;
	}

	@Override
	public synchronized TeamInt switchTeam(UserInt user, TeamInt team) {
		synchronized (user) {
			TeamInt oldTeam = findTeamOfUser(user);
			team.addUser(user);
			oldTeam.removeUser(user);
			System.out.println("User " + user.getUserName() + " has been moved from team " + oldTeam.getTeamName() + " to team " + findTeamOfUser(user).getTeamName() + ".");
		}
		return findTeamOfUser(user);
	}
	
	//print all Objects in physicalObjects
	public static boolean printPOList() {		
		System.out.println("Function deprecated");
		
		return true;
	}

	public static void printTeams() {
		int tSize = simulationImpl.getReference().getTeams().size();
		System.out.println("Size of teams: "+tSize);
		for(int i=0;i<tSize;i++){
			TeamInt teamTemp = simulationImpl.getReference().getTeams().get(i);
			
			System.out.print("["+i+"]\t" + "name: "+ teamTemp.getTeamName() + "\n");
							
			System.out.print("Users: \n");
				try {
					int countUsers = simulationImpl.getReference().getTeams().get(i).getUsers().size();
					if(countUsers>0){
						for(int j=0;j<countUsers;j++){
							System.out.println("UserID: "+ simulationImpl.getReference().getTeams().get(i).getUsers().get(j).getUserID() + "\t" + "Username: " + simulationImpl.getReference().getTeams().get(i).getUsers().get(j).getUserName() );
						}
					} else {
						System.out.println("There are no users available.");
					}
				} catch(Exception e) {
					System.out.println(e.getMessage());
				}
			System.out.print("Vehicles: \n");
				try {
					int countVehicles = simulationImpl.getReference().getTeams().get(i).getVehicles().size();
					if(countVehicles>0){
						System.out.println("Name:\t\tAltitude:\tPosition:\t\tDirection:\tHealth:\t\tSpeed:\t\tFuel:");
						for(int x=0;x<countVehicles;x++){
							System.out.println("Name: " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getName() + 
												"\t Altitude: " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getAltitude() + 
												"\t Position: " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getPosition().getX() + ", " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getPosition().getY() +
												"\t Direction: " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getDirection() + 
												"\t Health: " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getHealth() + 
												"\t Speed: " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getSpeed() +
												"\t Fuel: " + simulationImpl.getReference().getTeams().get(i).getVehicles().get(x).getFuel());
						}
					} else {
						System.out.println("There are no vehicles available.");
					}
				} catch(Exception e) {
					System.out.println(e.getMessage());
				}
			System.out.print("Projectiles: \n");
				try {
					int countProjectiles = simulationImpl.getReference().getTeams().get(i).getProjectiles().size();
					if(countProjectiles>0){
						System.out.println("Name:\t\tAltitude:\t\tDirection:\t\tMaxDist.\t\tSpeed:");
						for(int y=0;y<countProjectiles;y++){
							System.out.println("Name: " + simulationImpl.getReference().getTeams().get(i).getProjectiles().get(y).getName() +
											"\t Altitude: " + simulationImpl.getReference().getTeams().get(i).getProjectiles().get(y).getAltitude() +
											"\t Direction: " + simulationImpl.getReference().getTeams().get(i).getProjectiles().get(y).getDirection() +
											"\t Max Dist.: " + simulationImpl.getReference().getTeams().get(i).getProjectiles().get(y).getMaxDistance() +
											"\t Speed: " + simulationImpl.getReference().getTeams().get(i).getProjectiles().get(y).getSpeed());
						}
					} else {
						System.out.println("There are no projectiles available.");
					}
				} catch(Exception e) {
					System.out.println(e.getMessage());
				}
			System.out.print("Tracks: \n");
				try {
					int countTracks = simulationImpl.getReference().getTeams().get(i).getTracks().size();
					if(countTracks>0){
						System.out.println("Name:\tAltitude:\tDirection:\tSpeed:");
						for(int z=0;z<countTracks;z++){
							System.out.println("Name: " + simulationImpl.getReference().getTeams().get(i).getTracks().get(z).getName() +
											"\t Altitude: " + simulationImpl.getReference().getTeams().get(i).getTracks().get(z).getAltitude() +
											"\t Direction: " + simulationImpl.getReference().getTeams().get(i).getTracks().get(z).getDirection() +
											"\t Speed: " + simulationImpl.getReference().getTeams().get(i).getTracks().get(z).getSpeed());
						}
					} else {
						System.out.println("There are no tracks available.");
					}
				} catch(Exception e) {
					System.out.println(e.getMessage());
				}
		}
	}
	
	public static void printVehiclesListByTeams() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public RemoteList getTeams_Remote() {
		try {
			return new RemoteList(getTeams());
		} catch(Exception e){
			//
		}
		return new RemoteList();
	}

	@Override
	public Object userLogOn_Remote(String userName) {
		try {
			return new Remote(userLogOn(userName));
		} catch(Exception e){
			
		}
		return null;
	}

	@Override
	public Remote findTeamOfUser_Remote(UserInt user) {
		try {
			return new Remote(findTeamOfUser(user));
		} catch (RemoteException e) {
			System.out.println("ERROR: Update findTeamOfUser function failed");
		}
		return null;
	}

	@Override
	public Remote switchTeam_Remote(UserInt user, TeamInt team) {
		try {
			return new Remote(switchTeam(user, team));
		} catch (RemoteException e) {
			System.out.println("ERROR: Update switchTeam function failed");
		}
		return null;
	}

	@Override
	public RemoteList update_Remote(TeamInt team) {
		try {
			return new RemoteList(update(team));
		} catch (RemoteException e) {
			System.out.println("ERROR: Update update function failed");
		}
		return new RemoteList();
	}
	
}